package cn.virens;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.GroupContext;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.intf.Packet;
import org.tio.server.intf.ServerAioHandler;

import cn.virens.app.common.BytesBuilder;
import cn.virens.common.ByteUtil;
import cn.virens.packet.ErrorPacket;
import cn.virens.packet.ReadPacket;
import cn.virens.packet.WritePacket;
import cn.virens.packet.handler.AbstractHandler;

public class TioHandler implements ServerAioHandler {
	private static Logger logger = LoggerFactory.getLogger(TioHandler.class);

	private final Map<String, AbstractHandler> handlerMap = new HashMap<>();

	/**
	 * 添加包处理器
	 * 
	 * @param command 指令码
	 * @param handler 处理器
	 */
	public void addHandler(String command, AbstractHandler handler) {
		synchronized (handlerMap) {
			handlerMap.put(command, handler);
		}
	}

	@Override
	public Packet decode(ByteBuffer byteBuf, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
		try {
			logger.debug("指令地址为-->" + ByteUtil.toHex(new byte[] { (byte) byteBuf.get(6), (byte) byteBuf.get(7) }) + "数据包指令--->" + ByteUtil.toHex(byteBuf.array()));
			while ((position + 10) <= limit) {

				// 识别包
				int index = byteBuf.position();

				byte b1 = byteBuf.get(index++);
				byte b2 = byteBuf.get(index++);
				if (b1 == (byte) 0xdd && b2 == (byte) 0xaa) {
					if (byteBuf.remaining() < 6) {
						logger.error("长度不足:帧头不够");

						return null;
					}

					byte b3 = byteBuf.get(index + 0);
					byte b4 = byteBuf.get(index + 1);
					if (b3 == (byte) 0xdd && b4 == (byte) 0xaa) {
						byteBuf.position(index);
						continue;
					}

					ReadPacket packet = new ReadPacket();
					packet.seq(ByteUtil.toInt(new byte[] { (byte) byteBuf.get(index++) }));
					packet.ver(ByteUtil.toHex(new byte[] { (byte) byteBuf.get(index++) }));
					packet.len(ByteUtil.toInt(new byte[] { (byte) byteBuf.get(index++), (byte) byteBuf.get(index++) }));
					packet.cmd(ByteUtil.toHex(new byte[] { (byte) byteBuf.get(index++), (byte) byteBuf.get(index++) }));

					// 获取完整帧数据(会偏移byteBuf下标) 
					int length = packet.len();
					if (byteBuf.remaining() < length) {
						logger.error("长度不足:数据不够");

						return null;
					}

					byte[] bytes = new byte[length];
					for (int i = 0; i < length; i++) {
						bytes[i] = byteBuf.get();
					}

					// 获取数据包
					packet.data(Arrays.copyOfRange(bytes, 8, length - 2));

					// 获取CRC
					int crc1 = ByteUtil.toInt(bytes[length - 2], bytes[length - 1]);
					int crc2 = ByteUtil.toCRC(0xFFFF, 0xA001, Arrays.copyOfRange(bytes, 0, length - 2));

					if (crc1 != crc2) {
						return new ErrorPacket(bytes, "CRC校验失败");
					} else {
						return packet;
					}
				} else {
					byteBuf.position(++position);
				}
			}
		} catch (Exception e) {
			logger.error(e.getClass().getSimpleName() + "<--------lalalalala------>" + e.getMessage());
		}
		return null;
	}

	/**
	 * ff serial comm version length data crc ee
	 */
	@Override
	public ByteBuffer encode(Packet packet, GroupContext groupContext, ChannelContext channelContext) {
		if (packet instanceof WritePacket) {
			BytesBuilder builder = new BytesBuilder();
			WritePacket _packet = (WritePacket) packet;

			builder.append(new byte[] { (byte) 0xdd, (byte) 0xaa });
			builder.append(_packet.seq(), 1);
			builder.append(_packet.ver(), 1);
			builder.append(_packet.len(), 2);
			builder.append(_packet.cmd(), 2);
			builder.append(_packet.data());
			builder.append(toCRC(builder), 2);

			return ByteBuffer.wrap(builder.toArray());
		} else if (packet != null) {
			logger.error("待发送包不支持:" + packet.getClass().getName());
		} else {
			logger.error("待发送包为空 ");
		}

		return null;
	}

	@Override
	public void handler(Packet packet, ChannelContext channelContext) throws Exception {
		try {
			if (packet instanceof ReadPacket) {
				AbstractHandler handler = null;
				ReadPacket rp = (ReadPacket) packet;

				// 如果消息处理器不为空，就使用该消息处理器处理数据包
				if ((handler = handlerMap.get(rp.cmd())) != null) {
					handler.read(rp, channelContext);
				}
			} else {
				logger.error("无效包:" + String.valueOf(packet));
			}
		} catch (Exception e) {
			logger.error("包处理异常:" + e.getMessage(), e);
		}
	}

	private static int toCRC(BytesBuilder builder) {
		return ByteUtil.toCRC(0xFFFF, 0xA001, builder.toArray());
	}

}